<script lang="ts" setup>
import {SvgIcon} from '../svg.ts';
import {
  Chart,
  Tooltip,
  BarElement,
  LinearScale,
  TimeScale,
  type ChartOptions,
  type ChartData,
} from 'chart.js';
import {GET} from '../modules/fetch.ts';
import {Bar} from 'vue-chartjs';
import {
  startDaysBetween,
  firstStartDateAfterDate,
  fillEmptyStartDaysWithZeroes,
  type DayData,
  type DayDataObject,
} from '../utils/time.ts';
import {chartJsColors} from '../utils/color.ts';
import {sleep} from '../utils.ts';
import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm';
import {onMounted, ref, shallowRef} from 'vue';

const {pageData} = window.config;

Chart.defaults.color = chartJsColors.text;
Chart.defaults.borderColor = chartJsColors.border;

Chart.register(
  TimeScale,
  LinearScale,
  BarElement,
  Tooltip,
);

defineProps<{
  locale: {
    loadingTitle: string;
    loadingTitleFailed: string;
    loadingInfo: string;
  };
}>();

const isLoading = shallowRef(false);
const errorText = shallowRef('');
const repoLink = pageData.repoLink;
const data = ref<DayData[]>([]);

onMounted(() => {
  fetchGraphData();
});

async function fetchGraphData() {
  isLoading.value = true;
  try {
    let response: Response;
    do {
      response = await GET(`${repoLink}/activity/recent-commits/data`);
      if (response.status === 202) {
        await sleep(1000); // wait for 1 second before retrying
      }
    } while (response.status === 202);
    if (response.ok) {
      const dayDataObj: DayDataObject = await response.json();
      const start = Object.values(dayDataObj)[0].week;
      const end = firstStartDateAfterDate(new Date());
      const startDays = startDaysBetween(start, end);
      data.value = fillEmptyStartDaysWithZeroes(startDays, dayDataObj).slice(-52);
      errorText.value = '';
    } else {
      errorText.value = response.statusText;
    }
  } catch (err) {
    errorText.value = err.message;
  } finally {
    isLoading.value = false;
  }
}

function toGraphData(data: DayData[]): ChartData<'bar'> {
  return {
    datasets: [
      {
        // @ts-expect-error -- bar chart expects one-dimensional data, but apparently x/y still works
        data: data.map((i) => ({x: i.week, y: i.commits})),
        label: 'Commits',
        backgroundColor: chartJsColors['commits'],
        borderWidth: 0,
        tension: 0.3,
      },
    ],
  };
}

const options: ChartOptions<'bar'> = {
  responsive: true,
  maintainAspectRatio: false,
  scales: {
    x: {
      type: 'time',
      grid: {
        display: false,
      },
      time: {
        minUnit: 'week',
      },
      ticks: {
        maxRotation: 0,
        maxTicksLimit: 52,
      },
    },
    y: {
      ticks: {
        maxTicksLimit: 6,
      },
    },
  },
} satisfies ChartOptions;
</script>

<template>
  <div>
    <div class="ui header tw-flex tw-items-center tw-justify-between">
      {{ isLoading ? locale.loadingTitle : errorText ? locale.loadingTitleFailed: "Number of commits in the past year" }}
    </div>
    <div class="tw-flex ui segment main-graph">
      <div v-if="isLoading || errorText !== ''" class="tw-m-auto">
        <div v-if="isLoading">
          <SvgIcon name="gitea-running" class="tw-mr-2 rotate-clockwise"/>
          {{ locale.loadingInfo }}
        </div>
        <div v-else class="text red">
          <SvgIcon name="octicon-x-circle-fill"/>
          {{ errorText }}
        </div>
      </div>
      <Bar
        v-memo="data" v-if="data.length !== 0"
        :data="toGraphData(data)" :options="options"
      />
    </div>
  </div>
</template>
<style scoped>
.main-graph {
  height: 250px;
}
</style>
